package com.gitee.wsl.model.plugin

import com.gitee.wsl.model.Model
import com.gitee.wsl.prop.AttributeKey

/**
 * Client plugins factory.
 */
interface Plugin<TModel:Model<TModel>,
        PluginConfig,
        Builder:ModelPluginBuilder<TModel,PluginConfig,Builder>,
        PluginT> : ModelPlugin<TModel,PluginConfig, PluginT>

typealias CreateModelPluginInstanceBlock<TModel,PluginConfigT,Builder,PluginT> = (key: AttributeKey<ModelPluginInstance<TModel, PluginConfigT, Builder>>, configT: PluginConfigT, body: Builder.() -> Unit)-> PluginT

/**
 * Creates a [ClientPlugin] with empty config that can be installed into an [HttpClient].
 *
 * The example below creates a plugin that adds a custom header to every request:
 * ```
 * val CustomHeaderPlugin = createClientPlugin("CustomHeaderPlugin") {
 *     onRequest { request, _ ->
 *        request.headers.append("X-Custom-Header", "Custom-Header-Value")
 *     }
 * }
 *
 * client.install(CustomHeaderPlugin)
 * ```
 *
 * @param name A name of a plugin that is used to get its instance.
 * @param body Allows you to define handlers ([onRequest], [onResponse], and so on) that
 * can modify the behaviour of an [HttpClient] where your plugin is installed.
 **/
inline fun <reified TModel:Model<TModel>,
        reified Builder:ModelPluginBuilder<TModel,Unit,Builder>,
        reified PluginT:ModelPluginInstance<TModel,Unit,Builder>> createPlugin(
    name: String,
    noinline createModelPluginInstance :CreateModelPluginInstanceBlock<TModel,Unit,Builder,PluginT>,
    noinline body: Builder.() -> Unit
)  = createPlugin(name, {},createModelPluginInstance, body)

inline fun <reified TModel:Model<TModel>,
        PluginConfigT,
        reified Builder:ModelPluginBuilder<TModel,PluginConfigT,Builder>,
        reified PluginT:ModelPluginInstance<TModel,PluginConfigT,Builder>> createPlugin(
    name: String,
    noinline createConfiguration: () -> PluginConfigT,
    noinline createModelPluginInstance :CreateModelPluginInstanceBlock<TModel,PluginConfigT,Builder,PluginT>,
    noinline body: Builder.() -> Unit
) = object:PluginImpl<TModel,PluginConfigT,Builder,PluginT>(name, createConfiguration, createModelPluginInstance,body){

    override val key: AttributeKey<ModelPluginInstance<TModel, PluginConfigT, Builder>> = AttributeKey(name)

    override fun prepare(block: PluginConfigT.() -> Unit): PluginT {
        val config = createConfiguration().apply(block)
        return createModelPluginInstance(key,config,body)
        //return ModelPluginInstance(key, config, body)
    }
}


abstract class  PluginImpl<TModel:Model<TModel>, PluginConfigT,Builder:ModelPluginBuilder<TModel,PluginConfigT,Builder>,PluginT:ModelPluginInstance<TModel,PluginConfigT,Builder>>(
    name: String,
    private val createConfiguration: () -> PluginConfigT,
    private val createModelPluginInstance :CreateModelPluginInstanceBlock<TModel,PluginConfigT,Builder,PluginT>,
    private val body: Builder.() -> Unit
) : Plugin<TModel,PluginConfigT,Builder,ModelPluginInstance<TModel,PluginConfigT,Builder>> {

    //override val key: AttributeKey<PluginT> = AttributeKey<PluginT>(name)

//    override fun prepare(block: PluginConfigT.() -> Unit): PluginT {
//        val config = createConfiguration().apply(block)
//        return createModelPluginInstance(key,config,body)
//    }

    override fun install(plugin: ModelPluginInstance<TModel,PluginConfigT,Builder>, scope: TModel) {
        plugin.install(scope)
    }
}

